Make MLX metallib generation automatic and stale libraries loudly detectable - #131
Merged
Conversation
…ectable The AOT Metal kernel library (default.metallib) is not produced by `swift build` — there is no metallib rule in the SwiftPM manifest — yet the MLX runtime hard-requires it and loads whatever file it finds by name. The repo has vendored a copy (vendor/mlx-swift_Cmlx.bundle, consulted FIRST by both the CLI and test bundle resolvers) built from pre-0.30 mlx sources since the initial public release. mlx 0.31.x rewrote the 2-pass sdpa_vector kernel ABI, so once single-token decode crossed 1024 keys the mismatched kernels returned garbage, nondeterministically at temperature 0, for every MLX text model (Q35/ornith, Gemma4, LFM2, Psi) — while short prompts stayed clean (2026-07-03 incident). - scripts/build_mlx_metallib.sh: compile the AOT .metal sources from the current mlx-swift checkout with mlx's mandatory -fno-fast-math flags, link default.metallib, and stamp it (default.metallib.version) with the mlx core version, mlx-swift pin, and a kernel-source hash. Installs into .build (both configs) and refreshes the tracked vendor bundle; --verify-only checks an existing bundle against the checkout. - vendor/mlx-swift_Cmlx.bundle: regenerate the tracked metallib from mlx-swift 0.31.4 (mlx core 0.31.1) and add its version stamp. - MLXBundleSupport: validate the stamp at startup — hard error on mismatch (MERERUN_ALLOW_METALLIB_MISMATCH=1 override), loud warning when unstamped, self-heal by preferring stamp-matched candidate bundles, and atomically refresh the flat compatibility copies instead of skipping existing (stale) ones. A bundle husk without a metallib no longer counts as present. MLXRuntimeVersion (MereRunCore) exposes the compiled-in mlx core version via mlx-c for the comparison. - MLXTestSupport: replace stale colocated symlinks/copies instead of skipping them — a leftover absolute symlink into another checkout's vendored bundle pinned test runs to the broken metallib. - SDPAVectorKernelTests: GPU regression test on the exact failing shape (q [1,16,1,256] bf16, GQA 16:2, cache-style strided k/v views) across the 1024-key 2-pass dispatch boundary: unfused-fp32 agreement plus bit-identical determinism across repeated runs. Fails against the stale metallib, passes against a stamped fresh one. Run with MERERUN_TEST_MLX_DEVICE=gpu. - build_mere_run_app.sh: rebuild + stamp the metallib after swift build and refuse to package a bundle that fails --verify-only. install.sh: carry the stamp alongside the compatibility copies and warn when installing an unstamped library. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Plain `swift build` emits no mlx-swift_Cmlx.bundle on CI, so the metallib
script's mkdir-created skeleton lacked Contents/Info.plist and the app's
deep codesign seal rejected the bundle ("bundle format unrecognized,
invalid, or unsuitable"). Write a minimal resource-bundle Info.plist
whenever the bundle doesn't already have one. Verified locally by
deleting the bundle and running scripts/build_mere_run_app.sh debug
end-to-end (codesign --verify passes).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Incident
On 2026-07-03,
text chatat temperature 0 produced incoherent multilingual gibberish with run-to-run nondeterminism on any prompt past ~1024 tokens of total context, on every MLX text model (Q35/ornith, q36-nano, and by the same mechanism Gemma4/LFM2/Psi), while short prompts stayed perfectly clean. GGUF/llama.cpp models were unaffected.Root cause
swift buildnever compiles mlx-swift's Metal kernels — there is no metallib rule in the SwiftPM manifest — yet the MLX runtime hard-requiresdefault.metalliband loads whatever file it finds by name, with no version validation. The repo has vendored a copy (vendor/mlx-swift_Cmlx.bundle, tracked in git and consulted first by both the CLI bundle resolver and the test harness) that was built from pre-0.30 mlx sources at the initial public release and never regenerated.mlx 0.31.x rewrote the 2-pass
sdpa_vectorkernel ABI (ml-explore/mlx#3023: bf16 partials instead of fp32,gqa_factorbuffer removed, newblocksfunction constant). The 1-pass kernel ABI didn't change — so everything below 1024 keys worked — but the moment single-token decode crossed thek_len >= 1024two-pass dispatch threshold ('d'/'s'-class Apple GPUs, e.g. M4 Max), the 0.31.1 host code drove pre-0.30 kernel binaries: NaN/garbage partials, nondeterministic output. Isolated probe on the exact decode shape (q[1,16,1,256]bf16, GQA 16:2, cache-style strided k/v views): clean at N=1023,~1e34/NaN and run-to-run drift at N≥1024 with the stale lib; drift 0.0 and ref error ≤2e-4 at all N with a freshly compiled one.Skip-if-exists copy/symlink logic in
MLXBundleSupport,MLXTestSupport, andinstall.shthen propagated the stale blob into every build dir, install, xctest bundle, and release staging tree indefinitely.Changes
scripts/build_mlx_metallib.sh— compiles the AOT.metalsources from the current mlx-swift checkout with mlx's mandatory-fno-fast-mathflags, linksdefault.metallib, and writes adefault.metallib.versionsidecar (mlx core version, mlx-swift pin revision, kernel-source SHA-256). Installs into.build(both configs) and refreshes the tracked vendor bundle.--verify-only <bundle>exits non-zero on any drift from the checkout.vendor/mlx-swift_Cmlx.bundle— the tracked metallib regenerated from mlx-swift 0.31.4 (mlx core 0.31.1), now with its version stamp.MLXRuntimeVersion(MereRunCore) exposes the compiled-in mlx core version via mlx-c;MLXBundleSupport.ensureAvailablecompares it against the bundle stamp at startup: hard error on mismatch (override:MERERUN_ALLOW_METALLIB_MISMATCH=1), loud warning when unstamped, self-heal by preferring stamp-matched candidate bundles, atomic always-fresh compatibility copies (previously skip-if-exists, which let stale flat copies outlive bundle updates), and a bundle husk without a metallib no longer counts as present.MLXTestSupport— replaces stale colocated symlinks/copies instead of skipping them. A leftover absolute symlink into another checkout's vendored bundle had pinned test runs to the broken metallib; steady-state (symlink already pointing at the current source) remains a no-op.SDPAVectorKernelTests— GPU regression test on the exact failing shape across the 1024-key dispatch boundary (1023 control / 1100 / 3300): unfused-fp32 agreement plus bit-identical determinism over 3 runs. Fails against a stale metallib (verified live against two independent stale copies), passes against a stamped fresh one. Run withMERERUN_TEST_MLX_DEVICE=gpu.build_mere_run_app.shrebuilds + stamps the metallib afterswift buildand refuses to package a bundle failing--verify-only;install.shcarries the stamp alongside the compatibility copies and warns when installing an unstamped library.Verification
MERERUN_TEST_MLX_DEVICE=gpu swift test --filter "SDPAVectorKernelTests|Gemma4DecodeFusedKernelsTests": 6 tests, 0 failures.--verify-onlygreen on the committed vendor bundle; red on a tampered stamp.Notes for reviewers
build_mlx_metallib.sh --verify-only; that lives in the private repo and is not covered here.swift test -c releasefails to build (seedLoadedModelForTestingis debug-only).MERERUN_TEST_MLX_DEVICE=gpu.🤖 Generated with Claude Code